/*
 Copyright 2009 Abdulla Abdurakhmanov (abdulla@latestbit.com)
 Original sources are available at www.latestbit.com

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.

 BigDecimal.cpp. Created on: 01.03.2010 16:35:40 
 */

#include "cpputils/exttypes/BigDecimal.hpp"
#include "cpputils/exttypes/RationalNumberTools.hpp"

namespace cpputils {
namespace exttypes {

const BigDecimal BigDecimal::ZERO( 0L );
const BigDecimal BigDecimal::HALF( 1L, 2L );
const BigDecimal BigDecimal::ONE( 1L );
const BigDecimal BigDecimal::ONE_AND_HALF( 3L, 2L );
const BigDecimal BigDecimal::TWO( 2L );
const BigDecimal BigDecimal::TEN( 10L );
const BigDecimal BigDecimal::HUNDRED( 100L );

void BigDecimal::setupFromDecimalFormatString ( const std::string& sInputString, bool bPreInit, bool bNormalize ) {
	bool bSign;
	std::string sFirstValue;
	std::string sSecondValue;
	std::string sExponent;
	bool bExponentSign;

	RationalNumberTools::parseStringRationalFormat(sInputString, bSign, sFirstValue, sSecondValue, sExponent, bExponentSign);

	unsigned int nExponentValue = boost::lexical_cast<unsigned int>(sExponent);

	std::string sNumerator;
	sNumerator.reserve(16);
	std::string sDenominator;
	sDenominator.reserve(16);

	sNumerator = (bSign ? "":"-")+sFirstValue; //+sSecondValue;
	sDenominator = "1";

	bool bStillZero = true;
	for(unsigned int i = sSecondValue.size(); i > 0 ;  i--) {
		if(sSecondValue[i-1]=='0' && bStillZero)
			continue;
		bStillZero = false;
		sDenominator+="0";
	}

	for(unsigned int i = 0; i < sDenominator.size()-1 ;  i++) {
		sNumerator+=sSecondValue[i];
	}

	if(nExponentValue!=0) {
		if(bExponentSign) {
			for(unsigned int i = 0; i < nExponentValue; i++) {
				if(sDenominator.size() > 1)
					boost::algorithm::erase_tail(sDenominator, 1);
				else
					sNumerator+="0";
			}
		}
		else {
			for(unsigned int i = 0; i < nExponentValue; i++) {
				sDenominator+="0";
			}
		}
	}

	if(bPreInit)
		mpq_init(nValue);

	if(mpq_set_str(nValue, (sNumerator+"/"+sDenominator).c_str(), 10) < 0) {
		if(bPreInit)
			mpq_clear(nValue);
		throw SystemException("Decimal number format exception :" + (sNumerator+"/"+sDenominator));
	}

	if(bNormalize)
		normalize();
}

std::string BigDecimal::toDecimalFormatString(unsigned int nMaxPrecision) const {
	std::string sOutputValue;
	sOutputValue.reserve ( nMaxPrecision + 16 );

	unsigned int nIdx = 0;
	bool bHasDot = false;

	const BigInteger nValueNumerator( mpq_numref(nValue) );
	const BigInteger nValueDenominator( mpq_denref(nValue) );


	BigInteger nValDiv = nValueNumerator / nValueDenominator;
	BigInteger nValRemain = nValueNumerator % nValueDenominator;

	while(nIdx < nMaxPrecision) {

		sOutputValue+=nValDiv.toString();
		if(nValRemain!=BigInteger::ZERO) {
			nIdx++;
			if(!bHasDot) {
				sOutputValue+=".";
				bHasDot = true;
			}
			BigInteger nMulRemain = nValRemain * BigInteger::TEN;
			nValDiv =   nMulRemain / nValueDenominator;
			nValRemain = nMulRemain % nValueDenominator;
		}
		else
			break;
	}
	return sOutputValue;
}


void BigDecimal::normalize() {
	BigInteger nNumerator ( mpq_numref(nValue) );
	BigInteger nDenominator ( mpq_denref(nValue) );
	BigInteger nGCD;

	mpz_gcd ( nGCD.refValueInInternalFormat(), nNumerator.getValueInInternalFormat(), nDenominator.getValueInInternalFormat() );
	nNumerator/= nGCD;
	nDenominator/= nGCD;

	mpq_set_num(nValue, nNumerator.getValueInInternalFormat());
	mpq_set_den(nValue, nDenominator.getValueInInternalFormat());

}

}
}
